/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"RotatedBuffer.h"#include<sys/types.h> // for int32_t#include<algorithm> // for max#include"BasicImplData.h" // for BasicImplData#include"BasicLayersImpl.h" // for ToData#include"BufferUnrotate.h" // for BufferUnrotate#include"GeckoProfiler.h" // for AUTO_PROFILER_LABEL#include"Layers.h" // for PaintedLayer, Layer, etc#include"gfxPlatform.h" // for gfxPlatform#include"gfxPrefs.h" // for gfxPrefs#include"gfxUtils.h" // for gfxUtils#include"mozilla/ArrayUtils.h" // for ArrayLength#include"mozilla/gfx/BasePoint.h" // for BasePoint#include"mozilla/gfx/BaseRect.h" // for BaseRect#include"mozilla/gfx/BaseSize.h" // for BaseSize#include"mozilla/gfx/Matrix.h" // for Matrix#include"mozilla/gfx/Point.h" // for Point, IntPoint#include"mozilla/gfx/Rect.h" // for Rect, IntRect#include"mozilla/gfx/Types.h" // for ExtendMode::ExtendMode::CLAMP, etc#include"mozilla/layers/ShadowLayers.h" // for ShadowableLayer#include"mozilla/layers/TextureClient.h" // for TextureClient#include"mozilla/gfx/Point.h" // for IntSize#include"gfx2DGlue.h"#include"nsLayoutUtils.h" // for invalidation debuggingnamespacemozilla{usingnamespacegfx;namespacelayers{IntRectRotatedBuffer::GetQuadrantRectangle(XSideaXSide,YSideaYSide)const{// quadrantTranslation is the amount we translate the top-left// of the quadrant by to get coordinates relative to the layerIntPointquadrantTranslation=-mBufferRotation;quadrantTranslation.x+=aXSide==LEFT?mBufferRect.width:0;quadrantTranslation.y+=aYSide==TOP?mBufferRect.height:0;returnmBufferRect+quadrantTranslation;}RectRotatedBuffer::GetSourceRectangle(XSideaXSide,YSideaYSide)const{Rectresult;if(aXSide==LEFT){result.x=0;result.width=mBufferRotation.x;}else{result.x=mBufferRotation.x;result.width=mBufferRect.width-mBufferRotation.x;}if(aYSide==TOP){result.y=0;result.height=mBufferRotation.y;}else{result.y=mBufferRotation.y;result.height=mBufferRect.height-mBufferRotation.y;}returnresult;}/** * @param aXSide LEFT means we draw from the left side of the buffer (which * is drawn on the right side of mBufferRect). RIGHT means we draw from * the right side of the buffer (which is drawn on the left side of * mBufferRect). * @param aYSide TOP means we draw from the top side of the buffer (which * is drawn on the bottom side of mBufferRect). BOTTOM means we draw from * the bottom side of the buffer (which is drawn on the top side of * mBufferRect). */voidRotatedBuffer::DrawBufferQuadrant(gfx::DrawTarget*aTarget,XSideaXSide,YSideaYSide,ContextSourceaSource,floataOpacity,gfx::CompositionOpaOperator,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform)const{// The rectangle that we're going to fill. Basically we're going to// render the buffer at mBufferRect + quadrantTranslation to get the// pixels in the right place, but we're only going to paint within// mBufferRectIntRectquadrantRect=GetQuadrantRectangle(aXSide,aYSide);IntRectfillRect;if(!fillRect.IntersectRect(mBufferRect,quadrantRect))return;gfx::PointquadrantTranslation(quadrantRect.x,quadrantRect.y);MOZ_ASSERT(aSource!=BUFFER_BOTH);RefPtr<SourceSurface>snapshot=GetSourceSurface(aSource);if(!snapshot){gfxCriticalError()<<"Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";return;}// direct2d is much slower when using OP_SOURCE so use OP_OVER and// (maybe) a clear instead. Normally we need to draw in a single operation// (to avoid flickering) but direct2d is ok since it defers rendering.// We should try abstract this logic in a helper when we have other use// cases.if((aTarget->GetBackendType()==BackendType::DIRECT2D||aTarget->GetBackendType()==BackendType::DIRECT2D1_1)&&aOperator==CompositionOp::OP_SOURCE){aOperator=CompositionOp::OP_OVER;if(snapshot->GetFormat()==SurfaceFormat::B8G8R8A8){aTarget->ClearRect(IntRectToRect(fillRect));}}// OP_SOURCE is unbounded in Azure, and we really don't want that behaviour here.// We also can't do a ClearRect+FillRect since we need the drawing to happen// as an atomic operation (to prevent flickering).// We also need this clip in the case where we have a mask, since the mask surface// might cover more than fillRect, but we only want to touch the pixels inside// fillRect.aTarget->PushClipRect(IntRectToRect(fillRect));if(aMask){MatrixoldTransform=aTarget->GetTransform();// Transform from user -> buffer space.Matrixtransform=Matrix::Translation(quadrantTranslation.x,quadrantTranslation.y);MatrixinverseMask=*aMaskTransform;inverseMask.Invert();transform*=oldTransform;transform*=inverseMask;#ifdef MOZ_GFX_OPTIMIZE_MOBILESurfacePatternsource(snapshot,ExtendMode::CLAMP,transform,SamplingFilter::POINT);#elseSurfacePatternsource(snapshot,ExtendMode::CLAMP,transform);#endifaTarget->SetTransform(*aMaskTransform);aTarget->MaskSurface(source,aMask,Point(0,0),DrawOptions(aOpacity,aOperator));aTarget->SetTransform(oldTransform);}else{#ifdef MOZ_GFX_OPTIMIZE_MOBILEDrawSurfaceOptionsoptions(SamplingFilter::POINT);#elseDrawSurfaceOptionsoptions;#endifaTarget->DrawSurface(snapshot,IntRectToRect(fillRect),GetSourceRectangle(aXSide,aYSide),options,DrawOptions(aOpacity,aOperator));}aTarget->PopClip();}voidRotatedBuffer::DrawBufferWithRotation(gfx::DrawTarget*aTarget,ContextSourceaSource,floataOpacity,gfx::CompositionOpaOperator,gfx::SourceSurface*aMask,constgfx::Matrix*aMaskTransform)const{AUTO_PROFILER_LABEL("RotatedBuffer::DrawBufferWithRotation",GRAPHICS);// See above, in Azure Repeat should always be a safe, even faster choice// though! Particularly on D2D Repeat should be a lot faster, need to look// into that. TODO[Bas]DrawBufferQuadrant(aTarget,LEFT,TOP,aSource,aOpacity,aOperator,aMask,aMaskTransform);DrawBufferQuadrant(aTarget,RIGHT,TOP,aSource,aOpacity,aOperator,aMask,aMaskTransform);DrawBufferQuadrant(aTarget,LEFT,BOTTOM,aSource,aOpacity,aOperator,aMask,aMaskTransform);DrawBufferQuadrant(aTarget,RIGHT,BOTTOM,aSource,aOpacity,aOperator,aMask,aMaskTransform);}already_AddRefed<SourceSurface>SourceRotatedBuffer::GetSourceSurface(ContextSourceaSource)const{RefPtr<SourceSurface>surf;if(aSource==BUFFER_BLACK){surf=mSource;}else{MOZ_ASSERT(aSource==BUFFER_WHITE);surf=mSourceOnWhite;}MOZ_ASSERT(surf);returnsurf.forget();}/* static */boolRotatedContentBuffer::IsClippingCheap(DrawTarget*aTarget,constnsIntRegion&aRegion){// Assume clipping is cheap if the draw target just has an integer// translation, and the visible region is simple.return!aTarget->GetTransform().HasNonIntegerTranslation()&&aRegion.GetNumRects()<=1;}voidRotatedContentBuffer::DrawTo(PaintedLayer*aLayer,DrawTarget*aTarget,floataOpacity,CompositionOpaOp,SourceSurface*aMask,constMatrix*aMaskTransform){if(!EnsureBuffer()){return;}boolclipped=false;// If the entire buffer is valid, we can just draw the whole thing,// no need to clip. But we'll still clip if clipping is cheap ---// that might let us copy a smaller region of the buffer.// Also clip to the visible region if we're told to.if(!aLayer->GetValidRegion().Contains(BufferRect())||(ToData(aLayer)->GetClipToVisibleRegion()&&!aLayer->GetVisibleRegion().ToUnknownRegion().Contains(BufferRect()))||IsClippingCheap(aTarget,aLayer->GetLocalVisibleRegion().ToUnknownRegion())){// We don't want to draw invalid stuff, so we need to clip. Might as// well clip to the smallest area possible --- the visible region.// Bug 599189 if there is a non-integer-translation transform in aTarget,// we might sample pixels outside GetLocalVisibleRegion(), which is wrong// and may cause gray lines.gfxUtils::ClipToRegion(aTarget,aLayer->GetLocalVisibleRegion().ToUnknownRegion());clipped=true;}DrawBufferWithRotation(aTarget,BUFFER_BLACK,aOpacity,aOp,aMask,aMaskTransform);if(clipped){aTarget->PopClip();}}DrawTarget*RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(constIntRect&aBounds,ContextSourceaSource,DrawIterator*aIter){IntRectbounds=aBounds;if(aIter){// If an iterator was provided, then BeginPaint must have been run with// PAINT_CAN_DRAW_ROTATED, and the draw region might cover multiple quadrants.// Iterate over each of them, and return an appropriate buffer each time we find// one that intersects the draw region. The iterator mCount value tracks which// quadrants we have considered across multiple calls to this function.aIter->mDrawRegion.SetEmpty();while(aIter->mCount<4){IntRectquadrant=GetQuadrantRectangle((aIter->mCount&1)?LEFT:RIGHT,(aIter->mCount&2)?TOP:BOTTOM);aIter->mDrawRegion.And(aBounds,quadrant);aIter->mCount++;if(!aIter->mDrawRegion.IsEmpty()){break;}}if(aIter->mDrawRegion.IsEmpty()){returnnullptr;}bounds=aIter->mDrawRegion.GetBounds();}if(!EnsureBuffer()){returnnullptr;}MOZ_ASSERT(!mLoanedDrawTarget,"draw target has been borrowed and not returned");if(aSource==BUFFER_BOTH&&HaveBufferOnWhite()){if(!EnsureBufferOnWhite()){returnnullptr;}MOZ_ASSERT(mDTBuffer&&mDTBuffer->IsValid()&&mDTBufferOnWhite&&mDTBufferOnWhite->IsValid());mLoanedDrawTarget=Factory::CreateDualDrawTarget(mDTBuffer,mDTBufferOnWhite);}elseif(aSource==BUFFER_WHITE){if(!EnsureBufferOnWhite()){returnnullptr;}mLoanedDrawTarget=mDTBufferOnWhite;}else{// BUFFER_BLACK, or BUFFER_BOTH with a single buffer.mLoanedDrawTarget=mDTBuffer;}// Figure out which quadrant to draw inint32_txBoundary=mBufferRect.XMost()-mBufferRotation.x;int32_tyBoundary=mBufferRect.YMost()-mBufferRotation.y;XSidesideX=bounds.XMost()<=xBoundary?RIGHT:LEFT;YSidesideY=bounds.YMost()<=yBoundary?BOTTOM:TOP;IntRectquadrantRect=GetQuadrantRectangle(sideX,sideY);NS_ASSERTION(quadrantRect.Contains(bounds),"Messed up quadrants");mLoanedTransform=mLoanedDrawTarget->GetTransform();mLoanedDrawTarget->SetTransform(Matrix(mLoanedTransform).PreTranslate(-quadrantRect.x,-quadrantRect.y));returnmLoanedDrawTarget;}voidBorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*&aReturned){MOZ_ASSERT(mLoanedDrawTarget);MOZ_ASSERT(aReturned==mLoanedDrawTarget);if(mLoanedDrawTarget){mLoanedDrawTarget->SetTransform(mLoanedTransform);mLoanedDrawTarget=nullptr;}aReturned=nullptr;}gfxContentTypeRotatedContentBuffer::BufferContentType(){if(mBufferProvider||(mDTBuffer&&mDTBuffer->IsValid())){SurfaceFormatformat=SurfaceFormat::B8G8R8A8;if(mBufferProvider){format=mBufferProvider->GetFormat();}elseif(mDTBuffer&&mDTBuffer->IsValid()){format=mDTBuffer->GetFormat();}returnContentForFormat(format);}returngfxContentType::SENTINEL;}boolRotatedContentBuffer::BufferSizeOkFor(constIntSize&aSize){return(aSize==mBufferRect.Size()||(SizedToVisibleBounds!=mBufferSizePolicy&&aSize<mBufferRect.Size()));}boolRotatedContentBuffer::EnsureBuffer(){NS_ASSERTION(!mLoanedDrawTarget,"Loaned draw target must be returned");if(!mDTBuffer||!mDTBuffer->IsValid()){if(mBufferProvider){mDTBuffer=mBufferProvider->BorrowDrawTarget();}}NS_WARNING_ASSERTION(mDTBuffer&&mDTBuffer->IsValid(),"no buffer");return!!mDTBuffer;}boolRotatedContentBuffer::EnsureBufferOnWhite(){NS_ASSERTION(!mLoanedDrawTarget,"Loaned draw target must be returned");if(!mDTBufferOnWhite){if(mBufferProviderOnWhite){mDTBufferOnWhite=mBufferProviderOnWhite->BorrowDrawTarget();}}NS_WARNING_ASSERTION(mDTBufferOnWhite,"no buffer");return!!mDTBufferOnWhite;}boolRotatedContentBuffer::HaveBuffer()const{returnmBufferProvider||(mDTBuffer&&mDTBuffer->IsValid());}boolRotatedContentBuffer::HaveBufferOnWhite()const{returnmBufferProviderOnWhite||(mDTBufferOnWhite&&mDTBufferOnWhite->IsValid());}staticvoidWrapRotationAxis(int32_t*aRotationPoint,int32_taSize){if(*aRotationPoint<0){*aRotationPoint+=aSize;}elseif(*aRotationPoint>=aSize){*aRotationPoint-=aSize;}}staticIntRectComputeBufferRect(constIntRect&aRequestedRect){IntRectrect(aRequestedRect);// Set a minimum width to guarantee a minimum size of buffers we// allocate (and work around problems on some platforms with smaller// dimensions). 64 is the magic number needed to work around the// rendering glitch, and guarantees image rows can be SIMD'd for// even r5g6b5 surfaces pretty much everywhere.rect.width=std::max(aRequestedRect.width,64);returnrect;}voidRotatedContentBuffer::FlushBuffers(){if(mDTBuffer){mDTBuffer->Flush();}if(mDTBufferOnWhite){mDTBufferOnWhite->Flush();}}RotatedContentBuffer::PaintStateRotatedContentBuffer::BeginPaint(PaintedLayer*aLayer,uint32_taFlags){PaintStateresult;// We need to disable rotation if we're going to be resampled when// drawing, because we might sample across the rotation boundary.// Also disable buffer rotation when using webrender.boolcanHaveRotation=gfxPlatform::BufferRotationEnabled()&&!(aFlags&(PAINT_WILL_RESAMPLE|PAINT_NO_ROTATION))&&!(aLayer->Manager()->AsWebRenderLayerManager());nsIntRegionvalidRegion=aLayer->GetValidRegion();boolcanUseOpaqueSurface=aLayer->CanUseOpaqueSurface();ContentTypelayerContentType=canUseOpaqueSurface?gfxContentType::COLOR:gfxContentType::COLOR_ALPHA;SurfaceModemode;nsIntRegionneededRegion;IntRectdestBufferRect;boolcanReuseBuffer=HaveBuffer();while(true){mode=aLayer->GetSurfaceMode();neededRegion=aLayer->GetVisibleRegion().ToUnknownRegion();canReuseBuffer&=BufferSizeOkFor(neededRegion.GetBounds().Size());result.mContentType=layerContentType;if(canReuseBuffer){if(mBufferRect.Contains(neededRegion.GetBounds())){// We don't need to adjust mBufferRect.destBufferRect=mBufferRect;}elseif(neededRegion.GetBounds().Size()<=mBufferRect.Size()){// The buffer's big enough but doesn't contain everything that's// going to be visible. We'll move it.destBufferRect=IntRect(neededRegion.GetBounds().TopLeft(),mBufferRect.Size());}else{destBufferRect=neededRegion.GetBounds();}}else{// We won't be reusing the buffer. Compute a new rect.destBufferRect=ComputeBufferRect(neededRegion.GetBounds());}if(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA){#if defined(MOZ_GFX_OPTIMIZE_MOBILE)mode=SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;#elseif(!aLayer->GetParent()||!aLayer->GetParent()->SupportsComponentAlphaChildren()||!aLayer->AsShadowableLayer()||!aLayer->AsShadowableLayer()->HasShadow()){mode=SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;}else{result.mContentType=gfxContentType::COLOR;}#endif}if((aFlags&PAINT_WILL_RESAMPLE)&&(!neededRegion.GetBounds().IsEqualInterior(destBufferRect)||neededRegion.GetNumRects()>1)){// The area we add to neededRegion might not be painted opaquely.if(mode==SurfaceMode::SURFACE_OPAQUE){result.mContentType=gfxContentType::COLOR_ALPHA;mode=SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;}// We need to validate the entire buffer, to make sure that only valid// pixels are sampled.neededRegion=destBufferRect;}// If we have an existing buffer, but the content type has changed or we// have transitioned into/out of component alpha, then we need to recreate it.if(canReuseBuffer&&(result.mContentType!=BufferContentType()||(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA)!=HaveBufferOnWhite())){// Restart the decision process; we won't re-enter since we guard on// being able to re-use the buffer.canReuseBuffer=false;continue;}break;}if(HaveBuffer()&&(result.mContentType!=BufferContentType()||(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA)!=HaveBufferOnWhite())){// We're effectively clearing the valid region, so we need to draw// the entire needed region now.canReuseBuffer=false;result.mRegionToInvalidate=aLayer->GetValidRegion();validRegion.SetEmpty();Clear();#if defined(MOZ_DUMP_PAINTING)if(nsLayoutUtils::InvalidationDebuggingIsEnabled()){if(result.mContentType!=BufferContentType()){printf_stderr("Invalidating entire rotated buffer (layer %p): content type changed\n",aLayer);}elseif((mode==SurfaceMode::SURFACE_COMPONENT_ALPHA)!=HaveBufferOnWhite()){printf_stderr("Invalidating entire rotated buffer (layer %p): component alpha changed\n",aLayer);}}#endif}NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),"Destination rect doesn't contain what we need to paint");result.mRegionToDraw.Sub(neededRegion,validRegion);if(result.mRegionToDraw.IsEmpty())returnresult;if(HaveBuffer()){if(LockBuffers()){// Do not modify result.mRegionToDraw or result.mContentType after this call.// Do not modify mBufferRect, mBufferRotation, or mDidSelfCopy,// or call CreateBuffer before this call.FinalizeFrame(result.mRegionToDraw);}else{// Abandon everything and redraw it all. Ideally we'd reallocate and copy// the old to the new and then call FinalizeFrame on the new buffer so that// we only need to draw the latest bits, but we need a big refactor to support// that ordering.result.mRegionToDraw=neededRegion;canReuseBuffer=false;Clear();}}IntRectdrawBounds=result.mRegionToDraw.GetBounds();RefPtr<DrawTarget>destDTBuffer;RefPtr<DrawTarget>destDTBufferOnWhite;uint32_tbufferFlags=0;if(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA){bufferFlags|=BUFFER_COMPONENT_ALPHA;}if(canReuseBuffer){if(!EnsureBuffer()){returnresult;}IntRectkeepArea;if(keepArea.IntersectRect(destBufferRect,mBufferRect)){// Set mBufferRotation so that the pixels currently in mDTBuffer// will still be rendered in the right place when mBufferRect// changes to destBufferRect.IntPointnewRotation=mBufferRotation+(destBufferRect.TopLeft()-mBufferRect.TopLeft());WrapRotationAxis(&newRotation.x,mBufferRect.width);WrapRotationAxis(&newRotation.y,mBufferRect.height);NS_ASSERTION(gfx::IntRect(gfx::IntPoint(0,0),mBufferRect.Size()).Contains(newRotation),"newRotation out of bounds");int32_txBoundary=destBufferRect.XMost()-newRotation.x;int32_tyBoundary=destBufferRect.YMost()-newRotation.y;booldrawWrapsBuffer=(drawBounds.x<xBoundary&&xBoundary<drawBounds.XMost())||(drawBounds.y<yBoundary&&yBoundary<drawBounds.YMost());if((drawWrapsBuffer&&!(aFlags&PAINT_CAN_DRAW_ROTATED))||(newRotation!=IntPoint(0,0)&&!canHaveRotation)){// The stuff we need to redraw will wrap around an edge of the// buffer (and the caller doesn't know how to support that), so// move the pixels we can keep into a position that lets us// redraw in just one quadrant.if(mBufferRotation==IntPoint(0,0)){IntRectsrcRect(IntPoint(0,0),mBufferRect.Size());IntPointdest=mBufferRect.TopLeft()-destBufferRect.TopLeft();MOZ_ASSERT(mDTBuffer&&mDTBuffer->IsValid());mDTBuffer->CopyRect(srcRect,dest);if(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA){if(!EnsureBufferOnWhite()){returnresult;}MOZ_ASSERT(mDTBufferOnWhite&&mDTBufferOnWhite->IsValid());mDTBufferOnWhite->CopyRect(srcRect,dest);}result.mDidSelfCopy=true;mDidSelfCopy=true;// Don't set destBuffer; we special-case self-copies, and// just did the necessary work above.mBufferRect=destBufferRect;}else{// With azure and a data surface perform an buffer unrotate// (SelfCopy).unsignedchar*data;IntSizesize;int32_tstride;SurfaceFormatformat;if(mDTBuffer->LockBits(&data,&size,&stride,&format)){uint8_tbytesPerPixel=BytesPerPixel(format);BufferUnrotate(data,size.width*bytesPerPixel,size.height,stride,newRotation.x*bytesPerPixel,newRotation.y);mDTBuffer->ReleaseBits(data);if(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA){if(!EnsureBufferOnWhite()){returnresult;}MOZ_ASSERT(mDTBufferOnWhite&&mDTBufferOnWhite->IsValid());mDTBufferOnWhite->LockBits(&data,&size,&stride,&format);uint8_tbytesPerPixel=BytesPerPixel(format);BufferUnrotate(data,size.width*bytesPerPixel,size.height,stride,newRotation.x*bytesPerPixel,newRotation.y);mDTBufferOnWhite->ReleaseBits(data);}// Buffer unrotate moves all the pixels, note that// we self copied for SyncBackToFrontBufferresult.mDidSelfCopy=true;mDidSelfCopy=true;mBufferRect=destBufferRect;mBufferRotation=IntPoint(0,0);}if(!result.mDidSelfCopy){destBufferRect=ComputeBufferRect(neededRegion.GetBounds());CreateBuffer(result.mContentType,destBufferRect,bufferFlags,&destDTBuffer,&destDTBufferOnWhite);if(!destDTBuffer||(!destDTBufferOnWhite&&(bufferFlags&BUFFER_COMPONENT_ALPHA))){if(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width,destBufferRect.height))){gfxCriticalNote<<"Failed 1 buffer db="<<hexa(destDTBuffer.get())<<" dw="<<hexa(destDTBufferOnWhite.get())<<" for "<<destBufferRect.x<<", "<<destBufferRect.y<<", "<<destBufferRect.width<<", "<<destBufferRect.height;}returnresult;}}}}else{mBufferRect=destBufferRect;mBufferRotation=newRotation;}}else{// No pixels are going to be kept. The whole visible region// will be redrawn, so we don't need to copy anything, so we don't// set destBuffer.mBufferRect=destBufferRect;mBufferRotation=IntPoint(0,0);}}else{// The buffer's not big enough, so allocate a new oneCreateBuffer(result.mContentType,destBufferRect,bufferFlags,&destDTBuffer,&destDTBufferOnWhite);if(!destDTBuffer||(!destDTBufferOnWhite&&(bufferFlags&BUFFER_COMPONENT_ALPHA))){if(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width,destBufferRect.height))){gfxCriticalNote<<"Failed 2 buffer db="<<hexa(destDTBuffer.get())<<" dw="<<hexa(destDTBufferOnWhite.get())<<" for "<<destBufferRect.x<<", "<<destBufferRect.y<<", "<<destBufferRect.width<<", "<<destBufferRect.height;}returnresult;}}NS_ASSERTION(!(aFlags&PAINT_WILL_RESAMPLE)||destBufferRect==neededRegion.GetBounds(),"If we're resampling, we need to validate the entire buffer");// If we have no buffered data already, then destBuffer will be a fresh buffer// and we do not need to clear it below.boolisClear=!HaveBuffer();if(destDTBuffer){if(!isClear&&(mode!=SurfaceMode::SURFACE_COMPONENT_ALPHA||HaveBufferOnWhite())){// Copy the bitsIntPointoffset=-destBufferRect.TopLeft();Matrixmat=Matrix::Translation(offset.x,offset.y);destDTBuffer->SetTransform(mat);if(!EnsureBuffer()){returnresult;}MOZ_ASSERT(mDTBuffer&&mDTBuffer->IsValid(),"Have we got a Thebes buffer for some reason?");DrawBufferWithRotation(destDTBuffer,BUFFER_BLACK,1.0,CompositionOp::OP_SOURCE);destDTBuffer->SetTransform(Matrix());if(mode==SurfaceMode::SURFACE_COMPONENT_ALPHA){if(!destDTBufferOnWhite||!EnsureBufferOnWhite()){returnresult;}MOZ_ASSERT(mDTBufferOnWhite&&mDTBufferOnWhite->IsValid(),"Have we got a Thebes buffer for some reason?");destDTBufferOnWhite->SetTransform(mat);DrawBufferWithRotation(destDTBufferOnWhite,BUFFER_WHITE,1.0,CompositionOp::OP_SOURCE);destDTBufferOnWhite->SetTransform(Matrix());}}mDTBuffer=destDTBuffer.forget();mDTBufferOnWhite=destDTBufferOnWhite.forget();mBufferRect=destBufferRect;mBufferRotation=IntPoint(0,0);}NS_ASSERTION(canHaveRotation||mBufferRotation==IntPoint(0,0),"Rotation disabled, but we have nonzero rotation?");nsIntRegioninvalidate;invalidate.Sub(aLayer->GetValidRegion(),destBufferRect);result.mRegionToInvalidate.Or(result.mRegionToInvalidate,invalidate);result.mClip=DrawRegionClip::DRAW;result.mMode=mode;returnresult;}DrawTarget*RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState&aPaintState,DrawIterator*aIter/* = nullptr */){if(aPaintState.mMode==SurfaceMode::SURFACE_NONE){returnnullptr;}DrawTarget*result=BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),BUFFER_BOTH,aIter);if(!result){returnnullptr;}nsIntRegion*drawPtr=&aPaintState.mRegionToDraw;if(aIter){// The iterators draw region currently only contains the bounds of the region,// this makes it the precise region.aIter->mDrawRegion.And(aIter->mDrawRegion,aPaintState.mRegionToDraw);drawPtr=&aIter->mDrawRegion;}if(result->GetBackendType()==BackendType::DIRECT2D||result->GetBackendType()==BackendType::DIRECT2D1_1){// Simplify the draw region to avoid hitting expensive drawing paths// for complex regions.drawPtr->SimplifyOutwardByArea(100*100);}if(aPaintState.mMode==SurfaceMode::SURFACE_COMPONENT_ALPHA){if(!mDTBuffer||!mDTBuffer->IsValid()||!mDTBufferOnWhite||!mDTBufferOnWhite->IsValid()){// This can happen in release builds if allocating one of the two buffers// failed. This in turn can happen if unreasonably large textures are// requested.returnnullptr;}for(autoiter=drawPtr->RectIter();!iter.Done();iter.Next()){constIntRect&rect=iter.Get();mDTBuffer->FillRect(Rect(rect.x,rect.y,rect.width,rect.height),ColorPattern(Color(0.0,0.0,0.0,1.0)));mDTBufferOnWhite->FillRect(Rect(rect.x,rect.y,rect.width,rect.height),ColorPattern(Color(1.0,1.0,1.0,1.0)));}}elseif(aPaintState.mContentType==gfxContentType::COLOR_ALPHA&&HaveBuffer()){// HaveBuffer() => we have an existing buffer that we must clearfor(autoiter=drawPtr->RectIter();!iter.Done();iter.Next()){constIntRect&rect=iter.Get();result->ClearRect(Rect(rect.x,rect.y,rect.width,rect.height));}}returnresult;}already_AddRefed<SourceSurface>RotatedContentBuffer::GetSourceSurface(ContextSourceaSource)const{if(!mDTBuffer||!mDTBuffer->IsValid()){gfxCriticalNote<<"Invalid buffer in RotatedContentBuffer::GetSourceSurface "<<gfx::hexa(mDTBuffer);returnnullptr;}if(aSource==BUFFER_BLACK){returnmDTBuffer->Snapshot();}else{if(!mDTBufferOnWhite||!mDTBufferOnWhite->IsValid()){gfxCriticalNote<<"Invalid buffer on white in RotatedContentBuffer::GetSourceSurface "<<gfx::hexa(mDTBufferOnWhite);returnnullptr;}MOZ_ASSERT(aSource==BUFFER_WHITE);returnmDTBufferOnWhite->Snapshot();}}}// namespace layers}// namespace mozilla